﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;

namespace King.Extensions.Collections {

    public sealed class SynchronizedQueue<T> where T : class {

        private int? m_maxQueueSize;
        private ManualResetEvent m_itemAvailable;
        private ManualResetEvent m_spaceAvailable;
        private Queue<T> m_queue;

        public SynchronizedQueue(IEnumerable<T> collection = null, int? maxQueueSize = null) {
            Contract.Requires(maxQueueSize == null || maxQueueSize > 0);

            m_maxQueueSize = maxQueueSize;
            m_itemAvailable = new ManualResetEvent(false);
            m_spaceAvailable = new ManualResetEvent(true);
            m_queue = new Queue<T>(collection);
        }

        public int Count {
            get { lock (this) { return m_queue.Count; } }
        }
        public T Dequeue() {
            T result;

            while (!TryDequeue(out result))
                m_itemAvailable.WaitOne();

            return result;
        }
        public bool TryDequeue(out T result) {
            result = null;
            var success = false;

            lock (this) {

                if (m_queue.Count > 0) {
                    result = m_queue.Dequeue();
                    success = true;

                    if (m_queue.Count == 0)
                        m_itemAvailable.Reset();
                }
            }

            return success;
        }
        public void Enqueue(T item) {
            while (!TryEnqueue(item))
                m_spaceAvailable.WaitOne();
        }
        public bool TryEnqueue(T item) {
            var success = false;

            lock (this) {

                if (m_maxQueueSize != null && m_queue.Count < m_maxQueueSize) {
                    m_queue.Enqueue(item);
                    success = true;

                    if (m_maxQueueSize != null && m_queue.Count == m_maxQueueSize)
                        m_spaceAvailable.Reset();
                }
            }

            return success;
        }
        public T Peek() {
            lock (this) { return m_queue.Peek(); }
        }

        public WaitHandle ItemAvailable {
            get { return m_itemAvailable; }
        }
        public WaitHandle SpaceAvailable {
            get { return m_spaceAvailable; }
        }
    }

    public abstract class WeakDictionary<TKey, TValue> 
        : IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue> {

        #region Definitions
        private class Collect {

            #region Private Data Members
            private WeakDictionary<TKey, TValue> m_dictionary;
            #endregion

            #region Constructor
            internal Collect(WeakDictionary<TKey, TValue> dictionary) {
                m_dictionary = dictionary;
            }
            ~Collect() {
                if (AppDomain.CurrentDomain.IsFinalizingForUnload())
                    return;

                GC.ReRegisterForFinalize(this);

                Dictionary<object, object> dictionary = m_dictionary.m_dictionary;
                var pairs = new List<KeyValuePair<object, object>>();
                foreach (var pair in dictionary) {
                    WeakHashReference key = pair.Key as WeakHashReference;
                    WeakHashReference value = pair.Value as WeakHashReference;

                    if ((key != null && !key.IsAlive) ||
                        (value != null && !value.IsAlive))
                        pairs.Add(pair);
                }

                foreach (var pair in pairs)
                    dictionary.Remove(pair.Key);
            }
            #endregion
        }
        private class WeakHashReference {

            #region Private Data Members
            private WeakReference m_reference;
            private int m_hashcode;
            #endregion

            #region Constructor
            internal WeakHashReference(object value) {
                m_reference = new WeakReference(value, true);
                m_hashcode = value.GetHashCode();
            }
            #endregion

            #region Internal Members
            internal object Target { get { return m_reference.Target; } }
            internal bool IsAlive { get { return m_reference.IsAlive; } }
            #endregion

            #region Object Overrides
            public override string ToString() {
                object target = m_reference.Target;
                if (target == null)
                    return "null";
                return target.ToString();
            }
            public override bool Equals(object rhs) {
                object lhs = this;
                if (lhs == rhs)
                    return true;

                WeakHashReference weakRhs = rhs as WeakHashReference;
                if (weakRhs != null) {
                    rhs = weakRhs.Target;
                    if (rhs == null)
                        return false;
                }

                lhs = m_reference.Target;
                if (lhs == null)
                    return false;

                return lhs.Equals(rhs);
            }
            public override int GetHashCode() {
                return m_hashcode;
            }
            #endregion
        }
        private class Collection<T> : ICollection<T> {

            private IEnumerable<T> m_enumerable;

            internal Collection(IEnumerable<T> enumerable) {
                m_enumerable = enumerable;
            }

            public bool Contains(T item) {
                return m_enumerable.Contains(item);
            }
            public int Count {
                get { throw new InvalidOperationException(); }
            }
            public void Add(T item) {
                throw new NotImplementedException();
            }
            public void Clear() {
                throw new NotImplementedException();
            }
            public bool IsReadOnly {
                get { throw new NotImplementedException(); }
            }
            public void CopyTo(T[] array, int arrayIndex) {
                throw new NotImplementedException();
            }
            public bool Remove(T item) {
                throw new NotImplementedException();
            }

            public IEnumerator<T> GetEnumerator() {
                return m_enumerable.GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }
        #endregion

        #region Private Data Members
        private Dictionary<object, object> m_dictionary;
        private WeakReference m_collector;
        #endregion

        #region Constructor
        protected WeakDictionary(IEqualityComparer<TKey> comparer) {
            throw new NotImplementedException();
        }
        protected WeakDictionary() {
            m_dictionary = new Dictionary<object, object>();
            m_collector = new WeakReference(new Collect(this), true);
        }
        #endregion

        #region Private Members
        private void Update(int generation) {
            object target = m_collector.Target;
            if (target != null) {
                if (GC.GetGeneration(target) <= generation)
                    return;
                GC.SuppressFinalize(target);
            }

            m_collector = new WeakReference(new Collect(this), true);
        }
        private bool IsWeakKey { get { return this is WeakKeyDictionary<TKey, TValue>; } }
        private bool IsWeakValue { get { return this is WeakValueDictionary<TKey, TValue>; } }
        #endregion

        #region Public Members
        public bool Remove(TKey key) {
            object _key = IsWeakKey ? (object)new WeakHashReference(key) : (object)key;
            return m_dictionary.Remove(key);
        }
        public bool ContainsValue(object obj) {
            if (IsWeakKey) {
                return m_dictionary.ContainsValue(obj);
            } else {
                return m_dictionary.ContainsValue(new WeakHashReference(obj));
            }
        }
        public bool ContainsKey(TKey key) {
            TValue dummy;
            return TryGetValue(key, out dummy);
        }
        public bool TryGetValue(TKey key, out TValue value) {
            value = default(TValue);

            object _key = IsWeakKey ? (object)new WeakHashReference(key) : (object)key;
            object _value;
            if (!m_dictionary.TryGetValue(key, out _value))
                return false;

            WeakHashReference weakValue = _value as WeakHashReference;
            if (weakValue == null) {
                value = (TValue)_value;
                return true;
            }

            object reference = weakValue.Target;
            if (reference == null) {
                m_dictionary.Remove(key);
                return false;
            }

            value = (TValue)reference;
            return true;
        }
        public TValue this[TKey key] {
            get {
                TValue value;
                if (!TryGetValue(key, out value))
                    throw new KeyNotFoundException();
                return value;
            }
            set {
                Add(key, value);
            }
        }
        public void Add(TKey key, TValue value) {
            if (IsWeakKey)
                Update(GC.GetGeneration(key));

            if (IsWeakValue)
                Update(GC.GetGeneration(value));

            object _key = IsWeakKey ? (object)new WeakHashReference(key) : (object)key;
            object _value = IsWeakValue ? (object)new WeakHashReference(value) : (object)value;

            Debug.Assert(!m_dictionary.ContainsValue(_value));
            Debug.Assert(!m_dictionary.ContainsKey(_key));
            m_dictionary[_key] = _value;
        }
        public ICollection<TKey> Keys {
            get {
                var keys = (IEnumerable<object>)m_dictionary.Keys;
                if (IsWeakKey)
                    keys = m_dictionary.Keys.Cast<WeakHashReference>().Select(o => o.Target).WhereNotDefault();
                return new Collection<TKey>(keys.Cast<TKey>());
            }
        }
        public ICollection<TValue> Values {
            get {
                var values = (IEnumerable<object>)m_dictionary.Values;
                if (IsWeakValue)
                    values = m_dictionary.Values.Cast<WeakHashReference>().Select(o => o.Target).WhereNotDefault();
                return new Collection<TValue>(values.Cast<TValue>());
            }
        }
        public void Add(KeyValuePair<TKey, TValue> item) {
            Add(item.Key, item.Value);
        }
        public void Clear() {
            m_dictionary.Clear();
        }
        public bool Contains(KeyValuePair<TKey, TValue> item) {
            TValue value;
            if (!TryGetValue(item.Key, out value))
                return false;
            return object.Equals(item.Value, value);
        }
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            throw new NotImplementedException();
        }
        public int Count {
            get { throw new InvalidOperationException(); }
        }
        public bool IsReadOnly {
            get { return false; }
        }
        public bool Remove(KeyValuePair<TKey, TValue> item) {
            if (!Contains(item))
                return false;
            return Remove(item.Key);
        }
        #endregion

        #region IEnumerable<KeyValuePair<object,object>> Members
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            foreach (var pair in m_dictionary) {
                if (IsWeakKey) {
                    WeakHashReference weakKey = (WeakHashReference)pair.Key;
                    if (!weakKey.IsAlive)
                        continue;
                    yield return new KeyValuePair<TKey, TValue>((TKey)weakKey.Target, (TValue)pair.Value);
                } else {
                    WeakHashReference weakValue = (WeakHashReference)pair.Value;
                    if (!weakValue.IsAlive)
                        continue;
                    yield return new KeyValuePair<TKey, TValue>((TKey)pair.Key, (TValue)weakValue.Target);
                }
            }
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
        #endregion
    }

    public sealed class WeakKeyDictionary<TKey, TValue> : WeakDictionary<TKey, TValue> {

        #region Constructor
        public WeakKeyDictionary() { }
        public WeakKeyDictionary(Func<TKey, TKey, bool> equals, Func<TKey, int> getHashCode) 
            : base(EqualityComparer.Construct(equals, getHashCode)) { }
        #endregion
    }
    public sealed class WeakValueDictionary<TKey, TValue> : WeakDictionary<TKey, TValue> {

        #region Constructor
        public WeakValueDictionary() { }
        public WeakValueDictionary(Func<TKey, TKey, bool> equals, Func<TKey, int> getHashCode) 
            : base(EqualityComparer.Construct(equals, getHashCode)) { }
        #endregion
    }

    public static class EqualityComparer {

        public static IEqualityComparer<T> Construct<T>(Func<T, T, bool> equals, Func<T, int> getHashCode) {
            return new EqualityComparer<T>(equals, getHashCode);
        }
    }

    internal sealed class EqualityComparer<T> : IEqualityComparer<T> {

        // Private Data Members
        private Func<T, T, bool> m_equals;
        private Func<T, int> m_getHashCode;

        // EqualityComparer
        internal EqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode) {
            m_equals = equals;
            m_getHashCode = getHashCode;
        }

        // IEqualityComparer Members
        public new bool Equals(T x, T y) {
            return m_equals(x, y);
        }
        public int GetHashCode(T obj) {
            return m_getHashCode(obj);
        }
    }
}